﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;
using System.Xml.XPath;

namespace XExcel
{
    /// <summary>
    /// excel操作工具类
    /// </summary>
    public static partial class TableHelper
    {
        public static DataTable LoadFromHtml(string fileName, bool withHeader = true)
        {
            if(!File.Exists(fileName))
            {
                throw new IOException("文件不存在");
            }
            try
            {
                var content = ReadAllText(fileName);
                content = HttpUtility.HtmlDecode(content);
                var root = XElement.Parse(content);
                return LoadFromHtml(root, withHeader);
            }
            catch(Exception ex)
            {
                throw new IOException("加载文件错误", ex);
            }
        }

        public static DataTable LoadFromHtml(XElement root, bool withHeader = true)
        {
            try
            {
                var table = new DataTable();
                if(string.IsNullOrWhiteSpace(root.Value))
                {
                    return table;
                }

                var tab = Find(root, "table");
                if(tab != null)
                {
                    var trs = FindChidren(tab, "tr", new[] { "thead", "tbody" });

                    var rows = trs.Select(x => GetCellValues(x));

                    var firstRow = rows.FirstOrDefault();
                    var colCount = firstRow.Count();

                    if(withHeader)
                    {
                        foreach(var cell in firstRow)
                        {
                            table.Columns.Add(cell);
                        }
                        rows = rows.Skip(1);
                    }
                    else
                    {
                        var index = 0;
                        foreach(var cell in firstRow)
                        {
                            table.Columns.Add(index.ToString());
                            index++;
                        }
                    }

                    foreach(var row in rows)
                    {
                        DataRow csvDR = table.NewRow();
                        var cells = row.ToList();
                        for(int i = 0; i < cells.Count && i < colCount; i++)
                        {
                            csvDR[i] = cells[i];
                        }
                        table.Rows.Add(csvDR);
                    }
                }

                return table;
            }
            catch(Exception ex)
            {
                throw new IOException("读取文档错误", ex);
            }
        }

        private static IEnumerable<string> GetCellValues(XElement row)
        {
            var cells = row.XPathSelectElements("th");
            if(!cells.Any())
            {
                cells = row.XPathSelectElements("td");
            }
            foreach(var cell in cells)
            {
                var text = FormatCell(cell.Value);
                yield return text;
            }
        }

        private static XElement Find(XElement element, string tagName)
        {
            if(element.Name.LocalName.Equals(tagName, StringComparison.OrdinalIgnoreCase))
            {
                return element;
            }
            if(!element.HasElements)
            {
                return null;
            }

            var eles = element.Elements();
            foreach(var ele in eles)
            {
                var tag = Find(ele, tagName);
                if(tag != null)
                {
                    return tag;
                }
            }
            return null;
        }

        private static IEnumerable<XElement> FindChidren(XElement element, string tagName, string[] paths = null)
        {
            if(!element.HasElements)
            {
                yield break;
            }
            var elex = element.Elements();
            foreach(var ele in elex)
            {
                if(ele.Name.LocalName.Equals(tagName, StringComparison.CurrentCulture))
                {
                    yield return ele;
                }
                else
                {
                    if(paths == null || paths.Length == 0)
                    {
                        continue;
                    }
                    foreach(var path in paths)
                    {
                        if(ele.Name.LocalName.Equals(path, StringComparison.CurrentCulture))
                        {
                            var subeles = FindChidren(ele, tagName, null);
                            foreach(var subele in subeles)
                            {
                                yield return subele;
                            }
                        }
                    }
                }
            }
        }

        private static string FormatCell(string value)
        {
            if(string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            return HttpUtility.HtmlDecode(value);
        }

        /// <summary> 
        /// 通过给定的文件流，判断文件的编码类型 
        /// </summary> 
        /// <param name="fileName">文件流</param> 
        /// <returns>文件的编码类型</returns> 
        public static string ReadAllText(string fileName)
        {
            //byte[] Unicode = new byte[] { 0xFF, 0xFE };
            //byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF };
            //byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM 
            var encoding = Encoding.Default;
            using(var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                using(BinaryReader r = new BinaryReader(fs, encoding))
                {
                    int i;
                    int.TryParse(fs.Length.ToString(), out i);
                    byte[] ss = r.ReadBytes(i);
                    if((ss.Length > 3 && ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF) || IsUTF8Bytes(ss))
                    {
                        encoding = Encoding.UTF8;
                    }
                    else if(ss.Length > 3 && ss[0] == 0xFE && ss[1] == 0xFF)
                    {
                        encoding = Encoding.BigEndianUnicode;
                    }
                    else if(ss.Length > 3 && ss[0] == 0xFF && ss[1] == 0xFE)
                    {
                        encoding = Encoding.Unicode;
                    }
                }
            }
            return File.ReadAllText(fileName, encoding);
        }

        /// <summary> 
        /// 判断是否是不带 BOM 的 UTF8 格式 
        /// </summary> 
        /// <param name="data"></param> 
        /// <returns></returns> 
        private static bool IsUTF8Bytes(byte[] data)
        {
            int charByteCounter = 1; //计算当前正分析的字符应还有的字节数 
            byte curByte; //当前分析的字节. 
            for(int i = 0; i < data.Length; i++)
            {
                curByte = data[i];
                if(charByteCounter == 1)
                {
                    if(curByte >= 0x80)
                    {
                        //判断当前 
                        while(((curByte <<= 1) & 0x80) != 0)
                        {
                            charByteCounter++;
                        }
                        //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X 
                        if(charByteCounter == 1 || charByteCounter > 6)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    //若是UTF-8 此时第一位必须为1 
                    if((curByte & 0xC0) != 0x80)
                    {
                        return false;
                    }
                    charByteCounter--;
                }
            }
            if(charByteCounter > 1)
            {
                return false;
            }
            return true;
        }
    }
}